#!/usr/bin/env python
# -*- coding: UTF-8 -*-

import threading
import time

import serial
from scipy import interpolate
from simple_pid import PID
import matplotlib.pyplot as plt
import flowtransformer as ft

TIMER_OUT = 1

observer_value = [(0.06216, 150),
                  (0.06133, 145),
                  (0.06083, 140),
                  (0.05950, 135),
                  (0.05933, 125),
                  (0.05833, 120),
                  (0.05783, 115),
                  (0.05700, 110),
                  (0.05650, 105),
                  (0.05600, 100),
                  (0.05483, 95),
                  (0.05433, 90),
                  (0.05350, 85),
                  (0.05316, 80),
                  (0.05250, 75),
                  (0.05133, 70),
                  (0.05033, 65),
                  (0.04900, 60),
                  (0.04600, 55),
                  (0.04383, 50),
                  (0.04200, 45),
                  (0.04016, 40),
                  (0.03450, 35),
                  (0.02916, 30),
                  (0.02305, 25),
                  (0.00865, 20),
                  (0.00210, 15),
                  (0.00000, 10)]
flow_points = []
pwm_points = []
observer_value.reverse()
for part in observer_value:
    flow_points.append(part[0] * 10000)
    pwm_points.append(part[1])

max_flow = flow_points[len(flow_points) - 1]
min_pwm = pwm_points[0]
# 第一个是横坐标的值，第二个是纵坐标的值
plt.plot(pwm_points, flow_points)
plt.show()


class DiaphragmPumpDriver(threading.Thread):
    def __init__(self, port, baud_rate):
        threading.Thread.__init__(self, name="DiaphragmPumpDriver")
        self.my_serial = None
        self.__open_serial(port, baud_rate)
        self.tck = interpolate.splrep(flow_points, pwm_points)
        self.alive = False
        self.waitEnd = None
        self.pwm = 0  # 当前PWM值
        self.currentflow = 0.0
        self.buffer = ""
        self.lock = threading.Lock()  # 设置PWM的线程锁
        self.pid = PID(Kp=1.0, Ki=0.0, Kd=0.0, setpoint=0.0)
        self.hz = 4

    def __del__(self):
        self.shutdown()

    def __open_serial(self, port, baud_rate):
        try:
            self.my_serial = serial.Serial(port, baud_rate, timeout=TIMER_OUT)
            self.my_serial.bytesize = 8
            self.my_serial.parity = serial.PARITY_NONE
            self.my_serial.stopbits = 1
            if self.my_serial.isOpen():
                self.my_serial.close()
            self.my_serial.open()
            self.my_serial.write("V,000\n".encode("ASCII"))
        except Exception as message:
            print(message)

    def run(self):
        while self.my_serial.isOpen():
            self.waitEnd = threading.Event()
            self.alive = True
            s = time.time()
            self.reader()
            self.sender()
            e = time.time()
            sleeping = (1 / self.hz) - (e - s)
            if sleeping > 0:
                time.sleep(sleeping)

    def waiting(self):
        if not self.waitEnd is None:  # 等待event停止标志
            self.waitEnd.wait()

    # Q,0.000,F,0000,W,3.964,V,000\n
    def reader(self):
        if self.my_serial.readable():
            try:
                data = self.my_serial.read(self.my_serial.inWaiting())  # asii
                if len(data) > 0:
                    self.buffer += data.decode("utf-8")
                    tail = self.buffer.rfind("\n")
                    if tail is not -1:
                        qi = self.buffer.rfind("Q")
                        wi = self.buffer.rfind("W")
                        Q = float(self.buffer[qi + 2:qi + 7])
                        W = float(self.buffer[wi + 2:wi + 7])
                        print("Q:%f,W:%f" % (Q, W))
                        self.currentflow = Q / 60
                        self.buffer = self.buffer[tail:len(self.buffer)]
            except Exception as ex:
                print(ex)

    def sender(self):
        try:
            pidflow = self.pid(self.currentflow)
            print("currenrtflow:%f pidflow:%f result:%f setpoint:%f" % (
                self.currentflow, pidflow, self.currentflow + pidflow, self.pid.setpoint))
            self.pwm = self.pwm_sample((pidflow + self.currentflow) * 10000)  # 放大10000

            if self.pwm < min_pwm:
                self.pwm = 0

            str_cmd = "%d" % self.pwm

            str_cmd = "V,%s" % str_cmd.rjust(3, '0')
            self.my_serial.write(str_cmd.encode('ASCII'))
            print("PWM:%s" % str_cmd.encode('ASCII'))

        except Exception as exmessage:
            print(exmessage)

    def pwm_sample(self, flow):
        x = (flow if (abs(flow) < max_flow) else max_flow * flow / abs(flow))
        y = interpolate.splev(x, self.tck)  # 通过拟合的曲线，计算每一个输入值。
        return y

    def shutdown(self):
        cmd_data = "V,000\n"
        self.my_serial.write(cmd_data.encode("ASCII"))
        self.my_serial.close()

    def setflow(self, flow):
        with self.lock:
            print("回调触发： %f" % flow)
            self.pid.sample_time = 1 / self.hz
            self.pid.setpoint = flow

    def setflow_by_velocity(self, v=0.0):
        if v < ft.max_velocity:
            flow = ft.flowtransformer(v)
            self.setflow(flow)
        else:
            fps = ft.flowtransformer(ft.max_velocity)
            self.setflow(fps)

    def setpwm(self, pwm):
        with self.lock:
            self.pwm = int(self.pwm)

    def stop(self):
        pass


if __name__ == '__main__':
    port = 'COM7'
    baudrate = 115200
    ser = DiaphragmPumpDriver(port, baudrate)
    ser.start()

    speed = ft.max_velocity
    ser.setflow_by_velocity(float(speed))
    # while True:
    #     speed = input("input + speed:")
    #     ser.setflow_by_velocity(float(speed))
    #     time.sleep(60)
